package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"log"
	"net"
	"time"
)

//连接的配置
type ClientConfig struct {
	Host       string       //ip
	Port       int64        // 端口
	Username   string       //用户名
	Password   string       //密码
	Client	   *ssh.Client //ssh client
	LastResult string       //最近一次运行的结果
}

func (cliConf *ClientConfig) createClient(host string, port int64, username, password string) {
	var (
		client *ssh.Client
		err    error
	)
	cliConf.Host = host
	cliConf.Port = port
	cliConf.Username = username
	cliConf.Password = password

	//一般传入四个参数： user, []ssh.AuthMethod{ssh.Password(password)}, HostKeyCallback, 超时时间
	config := ssh.ClientConfig{
		User: cliConf.Username,
		Auth: []ssh.AuthMethod{ssh.Password(password)},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
		Timeout: 10 * time.Second,
	}
	addr := fmt.Sprintf("%s:%d", cliConf.Host, cliConf.Port)

	//获取client
	if client, err = ssh.Dial("tcp", addr, &config); err != nil {
		log.Fatalln("error occurred:", err)
	}

	cliConf.Client = client
}

func (cliConf *ClientConfig)RunShell(shell string) string {
    var (
    	session *ssh.Session
    	err     error
	)

    //获取session,这个session是用来远程执行操作的
    if session, err = cliConf.Client.NewSession(); err != nil {
    	log.Fatalln("error occurred:", err)
	}

	//执行shell
	if output, err := session.CombinedOutput(shell); err != nil {
		log.Fatalln("error occurred:", err)
	} else {
		cliConf.LastResult = string(output)
	}
	return cliConf.LastResult
}

func main()  {
	cliConf := new(ClientConfig)
	cliConf.createClient("47.100.237.45", 22, "root", "NewJb$LvbeX2R")
	/*
		可以看到我们这里每次执行一条命令都会创建一条session//host := "47.100.237.45:22"
		这是因为一条session默认只能执行一条命令////需要验证服务端，不做验证返回nil就可以，点击HostKeyCallback看源码就知道了
		并且两条命令不可以分开写//hostKeyCallbk  := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
		比如：//	return nil
		cliConf.RunShell("cd /opt")//}
		cliConf.RunShell("ls")//config := &ssh.ClientConfig{
		这两条命令是无法连续的，下面的ls查看的依旧是~目录//	User: "root",
		因此我们可以连着写，使用;分割//	Auth: []ssh.AuthMethod{
	*/
	shellStr := "cd /data/www/zhongnuo/admin; git pull; " +
		"cd /data/www/zhongnuo/pc; git pull; " +
		"cd /data/www/zhongnuo/zhongnuo; git pull;"
	fmt.Println(cliConf.RunShell(shellStr))
    
    // 延时（为了打包后的界面更友好）
    nowTime := time.Now()
    time.Sleep(2 * time.Second)
    latency := time.Since(nowTime).Seconds()
    fmt.Println(latency)
}
